﻿using Grow.Domain.EventBus.Cap.DomainEventHandlerInvoker;
using Grow.Module.Service;

namespace Grow.Domain.EventBus.Cap.SubscriberDomainEvents
{
    public class SubscriberDomainEventHandler : ISubscriberDomainEventHandler
    {
        private readonly IServiceContext _serviceContext;
        private readonly DomainEventBusOptions _domainEventBusOptions;
        private readonly IDomainEventHandlerInvoker _domainEventHandlerInvoker;
        public SubscriberDomainEventHandler(IServiceContext serviceContext)
        {
            _serviceContext = serviceContext;
            _domainEventBusOptions = _serviceContext.GetOptions<DomainEventBusOptions>();
            _domainEventHandlerInvoker = _serviceContext.GetRequiredService<IDomainEventHandlerInvoker>();
        }
        public async Task SubscriberAsync(SubscriberDomainEventDto subscriberDomainEventDto, CancellationToken cancellationToken = default)
        {
            int failCount = 1;
            while (!cancellationToken.IsCancellationRequested)
            {
                // 执行真正的消息发送
                if (!await EventHandlerInvokerAsync(subscriberDomainEventDto, cancellationToken))
                {
                    failCount++;
                }
                else
                {
                    return;
                }
                if (failCount > _domainEventBusOptions.RetryFailed)
                {
                    return;
                }
            }
        }

        private async Task<bool> EventHandlerInvokerAsync(SubscriberDomainEventDto subscriberDomainEventDto, CancellationToken cancellationToken)
        {
            try
            {
                if (await LockSubscriberEventAsync(subscriberDomainEventDto.Id, cancellationToken))
                {
                    // 这里可能存在的是消息发送成功,数据库更新失败,那么就可能存在重复发送的情况,这个需要消费方自行冥等处理
                    // 事务已提交,执行消息发送和更新状态
                    await _domainEventHandlerInvoker.InvokerAsync(subscriberDomainEventDto, cancellationToken);

                    // 消息发送没问题就更新数据库状态
                    await UpdateSubscriberEventAsync(subscriberDomainEventDto.Id, SubscriberDomainEventStatus.Success, cancellationToken);
                }
                return true;
            }
            catch
            {
                try
                {
                    await UpdateSubscriberEventAsync(subscriberDomainEventDto.Id, SubscriberDomainEventStatus.Fail, cancellationToken);
                }
                catch
                {
                }
                return false;
            }
        }
        private async Task<bool> LockSubscriberEventAsync(Guid Id, CancellationToken cancellationToken)
        {
            IServiceContext serviceContext = _serviceContext.GetServiceContext();
            ISubscriberDomainEventService subscriberDomainEventService = serviceContext.GetRequiredService<ISubscriberDomainEventService>();
            return await subscriberDomainEventService.LockSubscriberEventAsync(Id, cancellationToken);
        }



        private async Task UpdateSubscriberEventAsync(Guid Id, SubscriberDomainEventStatus subscriberDomainEventStatus, CancellationToken cancellationToken)
        {
            IServiceContext serviceContext = _serviceContext.GetServiceContext();
            ISubscriberDomainEventService subscriberDomainEventService = serviceContext.GetRequiredService<ISubscriberDomainEventService>();
            await subscriberDomainEventService.UpdateSubscriberEventAsync(Id, subscriberDomainEventStatus, cancellationToken);
        }
    }
}
